package com.googlecode.contests_repo.core.model;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

/**
 * Implements download operations for files and folders
 * 
 * @author Svetla Mrinova, M23322
 * 
 */
public class DownloadUtils {

	/**
	 * returns a list of downloadable resources for problem contained in a given
	 * problem directory
	 * 
	 * @param problemDir
	 * @return a list of downloadable resources for problem contained in a given
	 *         problem directory
	 */
	public static List<File> fetchDownloadableResources(File problemDir) {
		File[] listFiles = problemDir.listFiles(new FileFilter() {

			public boolean accept(File pathname) {
				if (pathname.isFile() && !pathname.getName().contains(".json")) {
					return true;
				}
				return false;
			}
		});
		List<File> result = new ArrayList<File>(listFiles.length);
		for (File file : listFiles) {
			result.add(file);
		}
		return result;
	}

	/**
	 * Downloads a single file
	 * 
	 * @param filePath
	 * @param ouputFileName
	 * @param response
	 */
	public static void downloadFile(String filePath, String ouputFileName,
			HttpServletResponse response) {
		response.setContentType("application/octet-stream");
		response.setHeader("Content-Disposition", "attachment;filename="
				+ ouputFileName);
		FileInputStream fileIn = null;
		ServletOutputStream output = null;
		try {
			fileIn = new FileInputStream(filePath);
			output = response.getOutputStream();
			byte[] outputByte = new byte[4096];
			// copy binary content to output stream
			while (fileIn.read(outputByte, 0, 4096) != -1) {
				output.write(outputByte, 0, 4096);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				fileIn.close();
				output.flush();
				output.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}

	/**
	 * Downloads a folder including its sub folders recursively
	 * 
	 * @param dirPath
	 * @param ouputFileName
	 * @param response
	 */
	public static void downloadFolder(String dirPath, String ouputFileName,
			HttpServletResponse response) {
		String outputZip = dirPath + File.separator + ".." + File.separator
				+ ouputFileName;
		try {
			ZipOutputStream outputStream = new ZipOutputStream(
					new FileOutputStream(outputZip));
			zipDir(dirPath, outputStream);
			// close the stream
			outputStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		downloadFile(outputZip, ouputFileName, response);
		File zipFile = new File(outputZip);
		zipFile.delete();
	}

	/**
	 * Zips folder including its sub folders recursively
	 * 
	 * @param dirToZip
	 * @param zipOuputStream
	 */
	public static void zipDir(String dirToZip, ZipOutputStream zipOuputStream) {
		try {
			File zipDir = new File(dirToZip);
			String[] dirList = zipDir.list();
			byte[] readBuffer = new byte[4096];
			int bytesIn = 0;
			// loop through dirList, and zip the files
			for (int i = 0; i < dirList.length; i++) {
				File currentFile = new File(zipDir, dirList[i]);
				if (currentFile.isDirectory()) {
					// if the File object is a directory, call this
					// function again to add its content recursively
					String filePath = currentFile.getPath();
					zipDir(filePath, zipOuputStream);
					continue;
				}
				FileInputStream inputStream = new FileInputStream(currentFile);
				ZipEntry anEntry = new ZipEntry(currentFile.getPath());
				// place the zip entry in the ZipOutputStream object
				zipOuputStream.putNextEntry(anEntry);
				// now write the content of the file to the ZipOutputStream
				while ((bytesIn = inputStream.read(readBuffer)) != -1) {
					zipOuputStream.write(readBuffer, 0, bytesIn);
				}
				inputStream.close();
			}
			if (dirList.length == 0) {
				ZipEntry anEntry = new ZipEntry(zipDir.getPath()
						+ File.separator);
				// place the zip entry in the ZipOutputStream object
				zipOuputStream.putNextEntry(anEntry);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
